package com.sdleyou.treepro.common.service.impl;

import com.sdleyou.treepro.common.exception.ServiceException;
import com.sdleyou.treepro.common.model.convert.AreaDO2AreaVOConverter;
import com.sdleyou.treepro.common.model.convert.MapDO2MapVOConverter;
import com.sdleyou.treepro.common.model.convert.RegionDO2RegionVOConverter;
import com.sdleyou.treepro.common.model.convert.WellDO2WellVOConverter;
import com.sdleyou.treepro.common.model.dao.*;
import com.sdleyou.treepro.common.model.dto.WellDTO;
import com.sdleyou.treepro.common.model.enums.ErrorCodeEnum;
import com.sdleyou.treepro.common.model.enums.MapShowEnum;
import com.sdleyou.treepro.common.model.enums.RedisLockEnum;
import com.sdleyou.treepro.common.model.enums.WellStatusEnum;
import com.sdleyou.treepro.common.model.vo.*;
import com.sdleyou.treepro.common.service.MongoPageManager;
import com.sdleyou.treepro.common.service.UserMapManager;
import com.sdleyou.treepro.common.service.UserTreeManager;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class UserMapManagerImpl implements UserMapManager {
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private MongoPageManager mongoPageManager;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private UserTreeManager userTreeManager;

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public List<MapVO> basicList() {
        Query query = new Query(Criteria.where("is_show").is(MapShowEnum.SHOW.name()));
        List<MapDO> mapDOList = mongoTemplate.find(query, MapDO.class);
        List<MapVO> mapVOList = mapDOList.stream().map(e -> MapDO2MapVOConverter.convert(e)).collect(Collectors.toList());
        return this.countBasicTree(mapVOList);
    }

    private List<MapVO> countBasicTree(List<MapVO> mapVOList) {
        for (int i = 0; i < mapVOList.size(); i++) {
            Integer total = 0;
            Integer current = 0;
            Aggregation aggregation = Aggregation.newAggregation(
                    Aggregation.match(Criteria.where("base_id").is(mapVOList.get(i).getBaseId())),
                    Aggregation.group("base_id").sum("$tree_sum").as("total"));
            AggregationResults<Map> ans = mongoTemplate.aggregate(aggregation, "{RRZS}_collection_map_region", Map.class);
            List<Map> mapList = ans.getMappedResults();
            if (mapList != null && mapList.size() > 0) {
                total += (Integer) mapList.get(0).get("total");
            }
            aggregation = Aggregation.newAggregation(
                    Aggregation.match(Criteria.where("base_id").is(mapVOList.get(i).getBaseId())),
                    Aggregation.group("base_id").sum("current_sum").as("current"));
            ans = mongoTemplate.aggregate(aggregation, "{RRZS}_collection_map_region", Map.class);
            mapList = ans.getMappedResults();
            if (mapList != null && mapList.size() > 0) {
                current += (Integer) mapList.get(0).get("current");
            }
            mapVOList.get(i).setTotalTree(total);
            mapVOList.get(i).setCurrentTree(current);
        }
        return mapVOList;
    }

    @Override
    public List<AreaVO> areaList(String baseId) {
        Query query = new Query(Criteria.where("is_show").is(MapShowEnum.SHOW.name()).and("base_id").is(baseId));
        List<AreaDO> areaDOList = mongoTemplate.find(query, AreaDO.class);
        List<AreaVO> areaVOList = areaDOList.stream().map(e -> AreaDO2AreaVOConverter.convert(e)).collect(Collectors.toList());
        for (int i = 0; i < areaVOList.size(); i++) {
            areaVOList.get(i).setHasFree(this.areaHasFree(areaVOList.get(i).getAreaId()));
        }
        return areaVOList;
    }

    @Override
    public Boolean areaHasFree(String areaId) {
        Query query = new Query(Criteria.where("area_id").is(areaId));
        List<RegionDO> regionDOList = mongoTemplate.find(query, RegionDO.class);
        for (RegionDO regionDO : regionDOList) {
            if (regionDO.getCurrentSum() < regionDO.getTreeSum()) {
                return true;
            }
        }
        return false;
    }

    @Override
    public Page<RegionVO> regionList(String areaId, String regionType, Integer pageNum, Integer pageSize) {
        Query query = new Query();
        Criteria c = Criteria.where("is_show").is("SHOW").and("area_id").is(areaId);
        Sort direction = Sort.by(Sort.Direction.ASC, "gmt_create");
        query.addCriteria(c).with(direction);
        Page<RegionDO> page = mongoPageManager.pageQuery(query, RegionDO.class, pageSize, pageNum);
        List<RegionDO> regionDOList = page.getData();
        List<RegionVO> regionVOList = regionDOList.stream().map(e -> RegionDO2RegionVOConverter.convert(e)).collect(Collectors.toList());
        return new Page<>(page.getPageNo(), page.getDataTotal(), page.getPageSize(), regionVOList);
    }

    @Override
    public Page<WellVO> wellList(String regionId, String status, Integer pageNum, Integer pageSize, String userId) {
        Query query = new Query();
        Criteria c = Criteria.where("region_id").is(regionId);
        if (status.equals(WellStatusEnum.OPEN.name())) {
            c.and("status").in(Arrays.asList("1", "2", "3"));
        }
        query.addCriteria(c);
        Page<WellDO> page = mongoPageManager.pageQuery(query, WellDO.class, pageSize, pageNum);
        List<WellDO> wellDOList = page.getData();
        List<WellVO> wellVOList = wellDOList.stream().map(e -> WellDO2WellVOConverter.convert(e)).collect(Collectors.toList());
        if (wellVOList != null && wellVOList.size() > 0) {
            for (int i = 0; i < wellVOList.size(); i++) {
                if (wellVOList.get(i).getUserId() != null && wellVOList.get(i).getUserId().equals(userId)) {
                    wellVOList.get(i).setIsMy(true);
                } else {
                    wellVOList.get(i).setIsMy(false);
                }
            }
        }
        return new Page<>(page.getPageNo(), page.getDataTotal(), page.getPageSize(), wellVOList);
    }

    @Override
    public void chooseWell(WellDTO wellDTO) {
        //如果有种植中的树坑，不能再次选择
        Query wellQuery = new Query(Criteria.where("user_id").is(wellDTO.getUserId()).and("is_finish").is(false));
        WellDO existWell = mongoTemplate.findOne(wellQuery, WellDO.class);
        if (existWell != null) {
            throw new ServiceException(ErrorCodeEnum.E020.code(), ErrorCodeEnum.E020.getDescription());
        }
        //获取内存锁
        RLock fairLock = redissonClient.getFairLock(RedisLockEnum.MAP_WELL.name());
        try {
            //加锁
            boolean res = fairLock.tryLock(2, 10, TimeUnit.SECONDS);
            if (res) {
                Query query = new Query(Criteria.where("well_id").is(wellDTO.getWellId()));
                WellDO wellDO = mongoTemplate.findOne(query, WellDO.class);
                if (wellDO == null) {
                    throw new ServiceException(ErrorCodeEnum.E011.code(), "坑位不存在");
                }
                if (!Arrays.asList("1", "2", "3").contains(wellDO.getStatus())) {
                    throw new ServiceException(ErrorCodeEnum.E012.code(), "此坑位不能选择");
                }
                Update update = new Update();
                update.set("user_id", wellDTO.getUserId());
                update.set("tree_name", wellDTO.getTreeName());
                update.set("tree_icon", wellDTO.getTreeIcon());
                update.set("tree_image", wellDTO.getTreeImage());
                Date date = new Date();
                update.set("choose_at", date.getTime());
                if (wellDO.getStatus().equals("1") || wellDO.getStatus().equals("3")) {
                    update.set("status", "4");
                }
                if (wellDO.getStatus().equals("2")) {
                    update.set("status", "5");
                }
                mongoTemplate.updateFirst(query, update, WellDO.class);
                Query queryRegion = new Query(Criteria.where("region_id").is(wellDO.getRegionId()));
                RegionDO regionDO = mongoTemplate.findOne(queryRegion, RegionDO.class);
                if (regionDO != null) {
                    //更新植树区树木记录
                    Update updateRegion = new Update();
                    updateRegion.set("current_sum", regionDO.getCurrentSum() + 1);
                    mongoTemplate.updateFirst(queryRegion, updateRegion, RegionDO.class);
                }
                //记录用户的当前坑位信息
                this.initUserWell(wellDO, wellDTO);
            }
        } catch (InterruptedException e) {
            logger.info("chooseWell 公平锁操作异常{}、{}", e.getStackTrace(), e.getMessage());
        } finally {
            //解锁
            fairLock.unlock();
        }
        return;
    }

    private void initUserWell(WellDO wellDO, WellDTO wellDTO) {
        Query queryBase = new Query(Criteria.where("base_id").is(wellDO.getBaseId()));
        MapDO mapDO = mongoTemplate.findOne(queryBase, MapDO.class);
        Query queryRegion = new Query(Criteria.where("region_id").is(wellDO.getRegionId()));
        RegionDO regionDO = mongoTemplate.findOne(queryRegion, RegionDO.class);
        Query queryArea = new Query(Criteria.where("area_id").is(wellDO.getAreaId()));
        AreaDO areaDO = mongoTemplate.findOne(queryArea, AreaDO.class);
        TreeWellDO treeWellDO = new TreeWellDO();
        treeWellDO.setBaseId(mapDO.getBaseId());
        treeWellDO.setBaseName(mapDO.getBaseName());
        treeWellDO.setAreaId(areaDO.getAreaId());
        treeWellDO.setAreaName(areaDO.getAreaName());
        treeWellDO.setRegionId(regionDO.getRegionId());
        treeWellDO.setRegionName(regionDO.getRegionName());
        treeWellDO.setWellId(wellDO.getWellId());
        treeWellDO.setWellName(wellDO.getWellName());
        treeWellDO.setLatitude(regionDO.getLatitude());
        treeWellDO.setLongitude(regionDO.getLongitude());
        treeWellDO.setBaseIcon(mapDO.getBaseIcon());
        Query queryUser = new Query(Criteria.where("user_id").is(wellDTO.getUserId()));
        Update update = new Update();
        update.set("tree_well", treeWellDO);
        mongoTemplate.updateFirst(queryUser, update, UserDO.class);
        return;
    }

    @Override
    public WellDTO quickChoose(String userId, String treeId) {
        Query queryUser = new Query(Criteria.where("user_id").is(userId));
        UserDO userDO = mongoTemplate.findOne(queryUser, UserDO.class);
        if (userDO == null) {
            throw new ServiceException(ErrorCodeEnum.E100.code(), ErrorCodeEnum.E100.getDescription());
        }
        Query queryTree = new Query(Criteria.where("tree_id").is(treeId));
        TreeDO treeDO = mongoTemplate.findOne(queryTree, TreeDO.class);
        if (treeDO == null) {
            throw new ServiceException(ErrorCodeEnum.E006.code(), ErrorCodeEnum.E006.getDescription());
        }
        WellDTO wellDTO = new WellDTO();
        //获取内存锁
        RLock fairLock = redissonClient.getFairLock(RedisLockEnum.MAP_WELL.name());
        try {
            boolean res = fairLock.tryLock(2, 10, TimeUnit.SECONDS);
            if (res) {
                Query queryWell = new Query(Criteria.where("status").in(Arrays.asList("2", "3")).and("pre_tree_id").is(treeId));
                WellDO wellDO = mongoTemplate.findOne(queryWell, WellDO.class);
                if (wellDO == null) {
                    throw new ServiceException(ErrorCodeEnum.E023.code(), ErrorCodeEnum.E023.getDescription());
                }
                Update update = new Update();
                update.set("user_id", userId);
                update.set("tree_name", treeDO.getTreeName());
                update.set("tree_icon", treeDO.getTreeIcon());
                update.set("tree_image", treeDO.getTreeImage());
                Date date = new Date();
                update.set("choose_at", date.getTime());
                if (wellDO.getStatus().equals("1") || wellDO.getStatus().equals("3")) {
                    update.set("status", "4");
                }
                if (wellDO.getStatus().equals("2")) {
                    update.set("status", "5");
                }
                mongoTemplate.updateFirst(queryWell, update, WellDO.class);
                Query queryRegion = new Query(Criteria.where("region_id").is(wellDO.getRegionId()));
                RegionDO regionDO = mongoTemplate.findOne(queryRegion, RegionDO.class);
                if (regionDO != null) {
                    //更新植树区树木记录
                    Update updateRegion = new Update();
                    updateRegion.set("current_sum", regionDO.getCurrentSum() + 1);
                    mongoTemplate.updateFirst(queryRegion, updateRegion, RegionDO.class);
                }
                Query queryBase = new Query(Criteria.where("base_id").is(regionDO.getBaseId()));
                MapDO mapDO = mongoTemplate.findOne(queryBase, MapDO.class);
                Query queryArea = new Query(Criteria.where("area_id").is(regionDO.getAreaId()));
                AreaDO areaDO = mongoTemplate.findOne(queryArea, AreaDO.class);

                wellDTO.setUserId(userId);
                wellDTO.setWellId(wellDO.getWellId());
                wellDTO.setWellName(wellDO.getWellName());
                wellDTO.setTreeId(treeId);
                wellDTO.setTreeName(treeDO.getTreeName());
                wellDTO.setTreeIcon(treeDO.getTreeIcon());
                wellDTO.setTreeImage(treeDO.getTreeImage());
                wellDTO.setLatitude(regionDO.getLatitude());
                wellDTO.setLongitude(regionDO.getLongitude());
                wellDTO.setBaseIcon(areaDO.getBaseIcon());
                wellDTO.setBaseId(mapDO.getBaseId());
                wellDTO.setBaseName(mapDO.getBaseName());
                wellDTO.setRegionId(regionDO.getRegionId());
                wellDTO.setRegionName(regionDO.getRegionName());
                //记录用户的当前坑位信息
                this.initUserWell(wellDO, wellDTO);
            }
        } catch (InterruptedException e) {
            logger.info("quickChoose 公平锁操作异常{}、{}", e.getStackTrace(), e.getMessage());
        } finally {
            //解锁
            fairLock.unlock();
        }
        return wellDTO;
    }

    /**
     * 查看已养成坑位信息
     *
     * @param userId
     * @return
     */
    @Override
    public List<WellOrderVO> getFinishWell(String userId) {
        Query queryWell = new Query(Criteria.where("status").is("6").and("is_finish").is(true).and("user_id").is(userId));
        List<WellDO> wellDOS = mongoTemplate.find(queryWell, WellDO.class);
        List<WellOrderVO> collect = wellDOS.stream().map(WellDO2WellVOConverter::convertOrderVO).collect(Collectors.toList());
        for (WellOrderVO wellOrderVO : collect) {
            if(0 == wellOrderVO.getOrderStatus()){
                //未预约
                Query queryBase = new Query(Criteria.where("base_id").is(wellOrderVO.getBaseId()));
                MapDO mapDO = mongoTemplate.findOne(queryBase, MapDO.class);
                //设置地址信息
                String address = mapDO.getBaseName();
                wellOrderVO.setAddress(address);
            }else {
                //已预约
                Query query = new Query(Criteria.where("appointment_id").is(wellOrderVO.getAppointmentId()));
                AppointmentDO appointmentDO = mongoTemplate.findOne(query, AppointmentDO.class);

                Query queryBase = new Query(Criteria.where("base_id").is(wellOrderVO.getBaseId()));
                MapDO mapDO = mongoTemplate.findOne(queryBase, MapDO.class);
                Query queryRegion = new Query(Criteria.where("region_id").is(wellOrderVO.getRegionId()));
                RegionDO regionDO = mongoTemplate.findOne(queryRegion, RegionDO.class);
                Query queryArea = new Query(Criteria.where("area_id").is(wellOrderVO.getAreaId()));
                AreaDO areaDO = mongoTemplate.findOne(queryArea, AreaDO.class);

                //设置地址信息
                String address = mapDO.getBaseName() + areaDO.getAreaName() + regionDO.getRegionName() + wellOrderVO.getWellName();
                wellOrderVO.setAddress(address);
                wellOrderVO.setOrderTime(appointmentDO.getOrderTime());
                //设置经纬度
                if(regionDO!=null) {
                    wellOrderVO.setLongitude(regionDO.getLongitude());
                    wellOrderVO.setLatitude(regionDO.getLatitude());
                }
            }
        }

        return collect;
    }

    /**
     * 更新坑位预约状态
     */
    @Override
    public void updateOrderStatus() {
        Query query = new Query(Criteria.where("order_status").is(null));
        List<WellDO> wellDOS = mongoTemplate.find(query, WellDO.class);
        for (WellDO wellDO : wellDOS) {
            if (wellDO != null) {
                Update update = new Update();
                update.set("order_status", 0);
                Query queryUser = new Query(Criteria.where("well_id").is(wellDO.getWellId()));
                mongoTemplate.updateFirst(queryUser, update, WellDO.class);
            }
        }
    }
}
